/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file graphic_geometry_depict_adaptor_vcgen.h
 *
 * @brief Defines Build geometry vertex generation process adapter
 * Use the coordinate conversion pipeline to change the vertices generated by the vertex source,
 * including coordinates, commands, generating new vertices, etc
 * @since 1.0
 * @version 1.0
 */

#ifndef GRAPHIC_LITE_DEPICT_ADAPTOR_VERTEX_GENERATE_H
#define GRAPHIC_LITE_DEPICT_ADAPTOR_VERTEX_GENERATE_H
#include <typeinfo>

#include "gfx_utils/diagram/common/common_basics.h"
#include "gfx_utils/diagram/vertexgenerate/vertex_generate_dash.h"

namespace OHOS {
/**
 * @struct EmptyMarkers
 * @brief Build a default empty geometry structure
 * This structure is mainly used to set the default empty tag symbol for elements such as stroke
 * @see template<class VertexSource, class Markers = EmptyMarkers>
 * struct DepictStroke
 * @since 1.0
 * @version 1.0
 */
struct EmptyMarkers {
    /**
     * @brief Clear all marker symbol elements
     * @since 1.0
     * @version 1.0
     */
    void RemoveAll() {}

    /**
     * @brief Add or insert marker symbol elements
     * @since 1.0
     * @version 1.0
     */
    void AddVertex(float, float, uint32_t) {}

    /**
     * @brief Make relevant preparations before using this structure to build markers symbols
     * @since 1.0
     * @version 1.0
     */
    void PrepareSrc() {}

    /**
     * @brief A bunch of operation processing resets the new state
     * @since 1.0
     * @version 1.0
     */
    void Rewind(uint32_t) {}

    /**
     * @brief Move vertices or generate new vertices according to different states
     * Return to the current operation status for subsequent processing
     * @since 1.0
     * @version 1.0
     */
    uint32_t GenerateVertex(float*, float*)
    {
        return PATH_CMD_STOP;
    }
};

/**
 * @template class DepictAdaptorVertexGenerator
 * @brief This template class is the base class of vertex generation adapter
 * It mainly uses the vertex construction and generation stage
 * Change the vertices generated by the vertex source,
 * including coordinates, commands, generating new vertices, etc.
 * template Parameters include vertex source, specific entity generator, marker symbol, etc
 * @since 1.0
 * @version 1.0
 */
template <class VertexSource,
          class Generator,
          class Markers = EmptyMarkers>
class DepictAdaptorVertexGenerate {
    /**
     * @brief Sets the state of the vertex adaptation generator
     * It basically includes initialization preparation,
     * cumulative generation of base points and generation of new point status of difference
     * @since 1.0
     * @version 1.0
     */
    enum VertexGenerator {
        INITIAL,
        ACCUMULATE,
        GENERATE
    };

public:
    /**
     * @brief The constructor of the template class DepictAdaptorVertexGenerator
     * Initialize the vertex source class, and the collocated state is the initial state
     * @since 1.0
     * @version 1.0
     */
    explicit DepictAdaptorVertexGenerate(VertexSource& source)
        : msource_(&source), status_(INITIAL), lastCmd_(0), startX_(0), startY_(0) {}
        
    /**
     * @brief Set vertex source directly
     * @since 1.0
     * @version 1.0
     */
    void Attach(VertexSource& source)
    {
        msource_ = &source;
    }

    /**
     * @brief Returns a variable generator reference
     * @since 1.0
     * @version 1.0
     */
    Generator& GetGenerator()
    {
        return generator_;
    }

    /**
     * @brief Returns an immutable generator reference
     * @since 1.0
     * @version 1.0
     */
    const Generator& GetGenerator() const
    {
        return generator_;
    }

    /**
     * @brief Returns a variable marker symbol reference
     * @since 1.0
     * @version 1.0
     */
    Markers& GetMarkers()
    {
        return markers_;
    }

    /**
     * @brief Returns an immutable marker symbol reference
     * @since 1.0
     * @version 1.0
     */
    const Markers& GetMarkers() const
    {
        return markers_;
    }

    /**
     * @brief Reset vertex state
     * @since 1.0
     * @version 1.0
     */
    void Rewind(uint32_t pathId)
    {
        msource_->Rewind(pathId);
        status_ = INITIAL;
    }

    /**
     * @brief Move vertices or generate new vertices according to different states
     * Return to the current operation status for subsequent processing
     * @since 1.0
     * @version 1.0
     */
    uint32_t GenerateVertex(float* x, float* y);
    /**
     * @brief Move vertices or generate new vertices according to accumulate state while func
     * @since 1.0
     * @version 1.0
     */
    void VertexAccumulateWhile(float* x, float* y);

private:
    DepictAdaptorVertexGenerate(const DepictAdaptorVertexGenerate<VertexSource, Generator, Markers>&);

    const DepictAdaptorVertexGenerate<VertexSource, Generator, Markers>& operator=
    (const DepictAdaptorVertexGenerate<VertexSource, Generator, Markers>&);

    VertexSource* msource_;
    Generator generator_;
    Markers markers_;
    VertexGenerator status_;
    uint32_t lastCmd_;
    float startX_;
    float startY_;
};

/**
 * @brief Move vertices or generate new vertices according to different states
 * Return to the current operation status for subsequent processing
 * @since 1.0
 * @version 1.0
 */
template <class VertexSource, class Generator, class Markers>
uint32_t DepictAdaptorVertexGenerate<VertexSource, Generator, Markers>::GenerateVertex(float* x, float* y)
{
    uint32_t cmd = PATH_CMD_STOP;
    bool done = false;
    while (!done) {
        switch (status_) {
            case INITIAL:
                lastCmd_ = msource_->GenerateVertex(&startX_, &startY_);
                markers_.RemoveAll();
                status_ = ACCUMULATE;
                break;
            case ACCUMULATE:
                if (IsStop(lastCmd_)) {
                    return PATH_CMD_STOP;
                }
                markers_.AddVertex(startX_, startY_, PATH_CMD_MOVE_TO);
#if defined(GRAPHIC_ENABLE_DASH_GENERATE_FLAG) && GRAPHIC_ENABLE_DASH_GENERATE_FLAG
                generator_.RemoveAll();
                generator_.AddVertex(startX_, startY_, PATH_CMD_MOVE_TO);

#else
                if (generator_.GetGenerateFlags() != VertexGenerateFlags::GENERATE_DASH) {
                    generator_.RemoveAll();
                    generator_.AddVertex(startX_, startY_, PATH_CMD_MOVE_TO);
                }
#endif
                VertexAccumulateWhile(x, y);
                if (generator_.GetGenerateFlags() != VertexGenerateFlags::GENERATE_DASH) {
                    generator_.Rewind(0);
                }
                status_ = GENERATE;
                break;
            case GENERATE:
                cmd = generator_.GenerateVertex(x, y);
                if (IsStop(cmd)) {
                    status_ = ACCUMULATE;
                    break;
                }
                done = true;
                break;
            default:
                break;
        }
    }
    return cmd;
}
/**
 * @brief Move vertices or generate new vertices according to accumulate state while func
 * @since 1.0
 * @version 1.0
 */
template <class VertexSource, class Generator, class Markers>
void DepictAdaptorVertexGenerate<VertexSource, Generator, Markers>::VertexAccumulateWhile(float* x,
                                                                                          float* y)
{
    uint32_t cmd = PATH_CMD_STOP;
    while (true) {
        cmd = msource_->GenerateVertex(x, y);
        if (IsVertex(cmd)) {
            lastCmd_ = cmd;
            if (IsMoveTo(cmd)) {
                startY_ = *y;
                startX_ = *x;
                break;
            }
            markers_.AddVertex(*x, *y, PATH_CMD_LINE_TO);
#if defined(GRAPHIC_ENABLE_DASH_GENERATE_FLAG) && GRAPHIC_ENABLE_DASH_GENERATE_FLAG
            generator_.AddVertex(*x, *y, cmd);
#else
            if (generator_.GetGenerateFlags() != VertexGenerateFlags::GENERATE_DASH) {
                generator_.AddVertex(*x, *y, cmd);
            }
#endif
        } else {
            if (IsStop(cmd)) {
                lastCmd_ = PATH_CMD_STOP;
                break;
            }
            if (IsEndPoly(cmd)) {
#if defined(GRAPHIC_ENABLE_DASH_GENERATE_FLAG) && GRAPHIC_ENABLE_DASH_GENERATE_FLAG
                generator_.AddVertex(*x, *y, cmd);
#else
                if (generator_.GetGenerateFlags() != VertexGenerateFlags::GENERATE_DASH) {
                    generator_.AddVertex(*x, *y, cmd);
                }

#endif
                break;
            }
        }
    }
}
} // namespace OHOS
#endif
